from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlalchemy.exc import OperationalError, SQLAlchemyError

from afast_core.core_plugins.db.sqlalchemy.dependencies_for_class_v2 import async_session_class_v2_dependency
from afast_core.core_plugins.db.sqlalchemy.transactional import AsyncSessionLoadMiddleware
from afast_core.core_plugins.pluginbase import IBasePlugin as BasePlugin
from pydantic import BaseSettings as Settings
from typing import Union, Optional, Callable, AsyncGenerator

from sqlalchemy.ext.asyncio import (
    AsyncSession,
    create_async_engine,
    async_scoped_session,
)
from sqlalchemy.orm import sessionmaker

# 自定义全局的变量的方式来使用对应的对象
# 上面全局定义的方式可以规避去除对应的通过上下文来依赖获取对象的方式
global_async_engine = None
global_async_session: Union[Callable, AsyncSession, AsyncGenerator, sessionmaker] = lambda: None


# 中间方式的实现
class SqlalchemyPluginClient(BasePlugin):
    # 设置插件默认的参数信息
    name = 'SqlalchemyPluginClient'

    # 内部配置项定义
    class SqlalchemySettings(Settings):
        # 数据库连接信息
        MYSQL_SERVER_HOST: str = "47.99.189.42:31166"
        MYSQL_USER_NAME: str = "root"
        MYSQL_PASSWORD: str = "xiaozhong123456"
        MYSQL_DB_NAME: str = "fastapi-jeecg-boot-admin"
        # 数据库连接池
        SQLALCHEMY_DATABASE_ECHO: bool = False
        SQLALCHEMY_POOL_RECYCLE: int = 7200
        SQLALCHEMY_POOL_PRE_PING: bool = True
        SQLALCHEMY_POOL_SIZE: int = 20
        SQLALCHEMY_MAX_OVERFLOW: int = 64

        @property
        def ASYNC_SQLALCHEMY_DATABASE_URI(self) -> Optional[str]:
            # 不要使用utf8
            return 'mysql+aiomysql://%s:%s@%s/%s?charset=UTF8MB4' % (
                self.MYSQL_USER_NAME,
                self.MYSQL_PASSWORD,
                self.MYSQL_SERVER_HOST,
                self.MYSQL_DB_NAME,
            )

    def setup(self, app: FastAPI, name: str = None, *args, **kwargs):
        """插件初始化"""
        self.app = app
        self._async_session: Union[AsyncSession, async_scoped_session, None] = None
        self.async_session_maker = None

        app.add_middleware(AsyncSessionLoadMiddleware)

        @app.on_event("startup")
        async def startup_event():
            try:
                # 创建异步会话对象
                global global_async_engine
                global global_async_session
                  # create_async_engine参数说明
                           # - database_url：用于创建引擎的数据库URL
                           # - pool_size：连接池大小
                           # - pool_recycle：重用连接池中的连接的时间（秒）
                           # - max_overflow：连接池中最多可以存放多少个连接
                           # - echo：是否记录日志
                           # create_async_engine() 方法用于创建一个异步数据引擎，参数如下：
                           #
                           # 1. **engine_str**：一个SQLAlchemy引擎字符串，用于构建数据库引擎。
                           # 2. **echo**：如果为True，则将SQLAlchemy的日志输出到标准输出流。
                           # 3. **pool_recycle**：用于控制连接池的回收间隔，以秒为单位。
                           # 4. **pool_size**：连接池的最大大小。
                           # 5. **max_overflow**：允许在连接池被耗尽时，池中可以创建的最大连接数。
                           # 6. **pool_timeout**：获取连接时的超时时间，以秒为单位。
                           # 7. **executor**：一个可选的执行器，用于运行异步操作，如果不指定，则使用默认的执行器。
                           # 8. **connect_args**：可选的连接参数，用于传递给引擎和连接池的connect()方法。
                self.async_engine = create_async_engine(url=self.settings.ASYNC_SQLALCHEMY_DATABASE_URI,
                                                        echo=self.settings.SQLALCHEMY_DATABASE_ECHO,
                                                        pool_recycle=self.settings.SQLALCHEMY_POOL_RECYCLE,
                                                        max_overflow=self.settings.SQLALCHEMY_MAX_OVERFLOW,
                                                        pool_pre_ping=self.settings.SQLALCHEMY_POOL_PRE_PING,
                                                        pool_size=self.settings.SQLALCHEMY_POOL_SIZE,
                                                        )
                # Sessionmaker参数如下：
                #
                # 1. bind：绑定一个engine或者connection，用来支持多个数据库的操作。
                #
                # 2. class_：自定义Session类。
                #
                # 3. autoflush：设置flush的时机，默认为True。
                #
                # 4. autocommit：设置commit的时机，默认为False。
                #
                # 5. expire_on_commit：设置在commit之后是否需要expire，默认为True。
                #
                # 6. query_cls：设置Query的class，默认为Query。
                #
                # 7. expire_on_commit：设置在commit之后是否需要expire，默认为True。
                #
                # 8. info：设置session的额外信息，默认为None。
                #
                # 9. extension：设置Session扩展，默认为None。
                #
                # 10. id：设置Session的id，默认为None。
                self.async_session_maker = sessionmaker(bind=self.async_engine,
                                                        class_=AsyncSession,
                                                        expire_on_commit=False)
                app.async_session = self
                app.async_session_maker = self.async_session_maker


            except Exception as e:
                raise e

        @app.on_event("shutdown")
        async def shutdown_event():
            pass


# ===============================================================
# 全局变量方式的实现
class SqlalchemyPluginForGlobalClient(BasePlugin):
    # 设置插件默认的参数信息
    name = 'SqlalchemyPluginClient'

    # 内部配置项定义
    class SqlalchemySettings(Settings):
        # 数据库连接信息
        MYSQL_SERVER_HOST: str = "47.99.189.42:31166"
        MYSQL_USER_NAME: str = "root"
        MYSQL_PASSWORD: str = "xiaozhong123456"
        MYSQL_DB_NAME: str = "fastapi-jeecg-boot-admin"
        # 数据库连接池
        SQLALCHEMY_DATABASE_ECHO: bool = False
        SQLALCHEMY_POOL_RECYCLE: int = 7200
        SQLALCHEMY_POOL_PRE_PING: bool = True
        SQLALCHEMY_POOL_SIZE: int = 20
        SQLALCHEMY_MAX_OVERFLOW: int = 64

        @property
        def ASYNC_SQLALCHEMY_DATABASE_URI(self) -> Optional[str]:
            # 不要使用utf8
            return 'mysql+aiomysql://%s:%s@%s/%s?charset=UTF8MB4' % (
                self.MYSQL_USER_NAME,
                self.MYSQL_PASSWORD,
                self.MYSQL_SERVER_HOST,
                self.MYSQL_DB_NAME,
            )

    def setup(self, app: FastAPI, name: str = None, *args, **kwargs):
        """插件初始化"""
        self.app = app
        self._async_session: Union[AsyncSession, async_scoped_session, None] = None
        self.async_session_maker = None

        @app.on_event("startup")
        async def startup_event():
            try:
                # 创建异步会话对象
                global global_async_engine
                global global_async_session
                self.async_engine = create_async_engine(url=self.settings.ASYNC_SQLALCHEMY_DATABASE_URI,
                                                        echo=self.settings.SQLALCHEMY_DATABASE_ECHO,
                                                        pool_recycle=self.settings.SQLALCHEMY_POOL_RECYCLE,
                                                        max_overflow=self.settings.SQLALCHEMY_MAX_OVERFLOW,
                                                        pool_pre_ping=self.settings.SQLALCHEMY_POOL_PRE_PING,
                                                        pool_size=self.settings.SQLALCHEMY_POOL_SIZE,
                                                        )

                self.async_session_maker = sessionmaker(bind=self.async_engine,
                                                        class_=AsyncSession,
                                                        expire_on_commit=False)
                app.async_session = self
                app.async_session_maker = self.async_session_maker
                # 全局变量的定义处理
                global_async_engine = self.async_engine
                global_async_session = self.async_session_maker

            except Exception as e:
                raise e

        @app.on_event("shutdown")
        async def shutdown_event():
            pass




# ========================================
# 类方式实现
# ========================================
class SqlalchemyPluginForClassV2Client(BasePlugin):
    # 设置插件默认的参数信息
    name = 'SqlalchemyPluginClient'

    # 内部配置项定义
    class SqlalchemySettings(Settings):
        # 数据库连接信息
        MYSQL_SERVER_HOST: str = "47.99.189.42:31166"
        MYSQL_USER_NAME: str = "root"
        MYSQL_PASSWORD: str = "xiaozhong123456"
        MYSQL_DB_NAME: str = "fastapi-jeecg-boot-admin"
        # 数据库连接池
        SQLALCHEMY_DATABASE_ECHO: bool = False
        SQLALCHEMY_POOL_RECYCLE: int = 7200
        SQLALCHEMY_POOL_PRE_PING: bool = True
        SQLALCHEMY_POOL_SIZE: int = 20
        SQLALCHEMY_MAX_OVERFLOW: int = 64

        @property
        def ASYNC_SQLALCHEMY_DATABASE_URI(self) -> Optional[str]:
            # 不要使用utf8
            return 'mysql+aiomysql://%s:%s@%s/%s?charset=UTF8MB4' % (
                self.MYSQL_USER_NAME,
                self.MYSQL_PASSWORD,
                self.MYSQL_SERVER_HOST,
                self.MYSQL_DB_NAME,
            )

    def setup(self, app: FastAPI, name: str = None, *args, **kwargs):
        """插件初始化"""
        self.app = app
        self._async_session: Union[AsyncSession, async_scoped_session, None] = None
        self.async_session_maker = None

        @app.on_event("startup")
        async def startup_event():
            try:
                # 创建异步会话对象
                global global_async_engine
                global global_async_session
                self.async_engine = create_async_engine(url=self.settings.ASYNC_SQLALCHEMY_DATABASE_URI,
                                                        echo=self.settings.SQLALCHEMY_DATABASE_ECHO,
                                                        pool_recycle=self.settings.SQLALCHEMY_POOL_RECYCLE,
                                                        max_overflow=self.settings.SQLALCHEMY_MAX_OVERFLOW,
                                                        pool_pre_ping=self.settings.SQLALCHEMY_POOL_PRE_PING,
                                                        pool_size=self.settings.SQLALCHEMY_POOL_SIZE,
                                                        )

                self.async_session_maker = sessionmaker(bind=self.async_engine,
                                                        class_=AsyncSession,
                                                        expire_on_commit=False)
                app.async_session = self
                app.async_session_maker = self.async_session_maker

                # 初始化类V2版本方式的依赖项
                await async_session_class_v2_dependency.setup_on_event(
                    _engine=self.async_engine,
                    _session=self.async_session_maker
                )
            except Exception as e:
                raise e

        @app.on_event("shutdown")
        async def shutdown_event():
            pass
            await async_session_class_v2_dependency.close_on_event()

# qjljdppbrgykbijg